Skip to content

feat: implement entry for BTreeMap similar to std lib#420

Open
hpeebles wants to merge 18 commits intodfinity:mainfrom
hpeebles:entry
Open

feat: implement entry for BTreeMap similar to std lib#420
hpeebles wants to merge 18 commits intodfinity:mainfrom
hpeebles:entry

Conversation

@hpeebles
Copy link
Copy Markdown
Contributor

@hpeebles hpeebles commented Mar 26, 2026

This allows you to lookup an entry by searching the map, then modify the value and insert it back into the map without having to search again.

The API is similar to entry on the std lib BTreeMap, but is not able to return mutable references, so you need to either use and_modify or get the owned value, update it, then insert it back in the entry.

For example in the std lib you would write this

match map.entry(1) {
    Occupied(mut e) => {
        let value = e.get_mut();
        *value += 1;
    }
    Vacant(e) => {
        e.insert(1);
    }
}

Or using the shorthand syntax

map.entry(1).and_modify(|v| *v += 1).or_insert(1)

Whereas using this new API for StableBTreeMap you would write it as this

match map.entry(1) {
    Occupied(e) => {
        let value = e.get();
        e.insert(value + 1);
    }
    Vacant(e) => {
        e.insert(1);
    }
}

Or using the shorthand syntax

map.entry(1).and_modify(|v| *v += 1).or_insert(1)

I've added some benchmarks which compare using get then insert vs using entry for 10k u32 values, from the results you can see that in this scenario using entry is roughly 37% faster

| status | name                            | calls |     ins |  ins Δ% | HI |  HI Δ% | SMI |  SMI Δ% |
|--------|---------------------------------|-------|---------|---------|----|--------|-----|---------|
|  new   | btreemap_get_and_incr           |       | 597.30M |         |  0 |        |   0 |         |
|  new   | btreemap_get_and_incr_via_entry |       | 374.19M |         |  0 |        |   0 |         |

@hpeebles hpeebles requested a review from a team as a code owner March 26, 2026 17:53
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 27, 2026

canbench 🏋 (dir: ./benchmarks/nns) c2e383e 2026-04-01 15:05:02 UTC

./benchmarks/nns/canbench_results.yml is up to date
📦 canbench_results_nns.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 27, 2026

canbench 🏋 (dir: ./benchmarks/io_chunks) c2e383e 2026-04-01 15:05:38 UTC

./benchmarks/io_chunks/canbench_results.yml is up to date
📦 canbench_results_io_chunks.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 18 | regressed 0 | improved 0 | new 0 | unchanged 18]
    change:   [max +5 | p75 0 | median 0 | p25 -11 | min -106.31M]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 -0.00% | min -0.73%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 18 | regressed 0 | improved 0 | new 0 | unchanged 18]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 18 | regressed 0 | improved 0 | new 0 | unchanged 18]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 27, 2026

canbench 🏋 (dir: ./benchmarks/vec) c2e383e 2026-04-01 15:04:51 UTC

./benchmarks/vec/canbench_results.yml is up to date
📦 canbench_results_vec.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 16 | regressed 0 | improved 0 | new 0 | unchanged 16]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 27, 2026

canbench 🏋 (dir: ./benchmarks/memory_manager) c2e383e 2026-04-01 15:04:51 UTC

./benchmarks/memory_manager/canbench_results.yml is up to date
📦 canbench_results_memory-manager.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 3 | regressed 0 | improved 0 | new 0 | unchanged 3]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 3 | regressed 0 | improved 0 | new 0 | unchanged 3]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 3 | regressed 0 | improved 0 | new 0 | unchanged 3]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 27, 2026

canbench 🏋 (dir: ./benchmarks/btreeset) c2e383e 2026-04-01 15:05:20 UTC

./benchmarks/btreeset/canbench_results.yml is up to date
📦 canbench_results_btreeset.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 100 | regressed 0 | improved 0 | new 0 | unchanged 100]
    change:   [max +42.83M | p75 0 | median 0 | p25 0 | min 0]
    change %: [max +0.59% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 100 | regressed 0 | improved 0 | new 0 | unchanged 100]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 100 | regressed 0 | improved 0 | new 0 | unchanged 100]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
CSV results saved to canbench_results.csv

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 27, 2026

canbench 🏋 (dir: ./benchmarks/btreemap) c2e383e 2026-04-01 15:06:22 UTC

./benchmarks/btreemap/canbench_results.yml is not up to date
If the performance change is expected, run canbench --persist [--csv] to update the benchmark results.
📦 canbench_results_btreemap.csv available in artifacts

---------------------------------------------------

Summary:
  instructions:
    status:   New benchmarks added ➕
    counts:   [total 231 | regressed 0 | improved 0 | new 2 | unchanged 229]
    change:   [max +44.17M | p75 +2.87M | median +327.23K | p25 0 | min -8.34M]
    change %: [max +0.83% | p75 +0.39% | median +0.02% | p25 0.00% | min -0.34%]

  heap_increase:
    status:   New benchmarks added ➕
    counts:   [total 231 | regressed 0 | improved 0 | new 2 | unchanged 229]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   New benchmarks added ➕
    counts:   [total 231 | regressed 0 | improved 0 | new 2 | unchanged 229]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------

Only significant changes:
| status | name                            | calls |     ins |  ins Δ% | HI |  HI Δ% | SMI |  SMI Δ% |
|--------|---------------------------------|-------|---------|---------|----|--------|-----|---------|
|  new   | btreemap_get_and_incr           |       | 544.38M |         |  0 |        |   0 |         |
|  new   | btreemap_get_and_incr_via_entry |       | 376.51M |         |  0 |        |   0 |         |

ins = instructions, HI = heap_increase, SMI = stable_memory_increase, Δ% = percent change

---------------------------------------------------
CSV results saved to canbench_results.csv

NodeType::Internal => {
match node.search(key, self.memory()) {
Ok(idx) => {
// Case 2: The node is an internal node and the key exists in it.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is unchanged, it has just moved to remove_from_internal_node so that it can be used within entry.rs

NodeType::Leaf => {
match node.search(key, self.memory()) {
Ok(idx) => {
// Case 1: The node is a leaf node and the key exists in it.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is unchanged, it has just moved to remove_from_leaf_node so that it can be used within entry.rs

@hpeebles
Copy link
Copy Markdown
Contributor Author

hpeebles commented Apr 1, 2026

The insert_nonfull function has been renamed to find_node_for_insert which now takes a callback.
Within insert the callback will do the insertion, so after the compiler does its monomorphisation, the end result should match the previous implementation.
Within entry the callback returns the values needed to construct either an OccupiedEntry or VacantEntry.

@hpeebles
Copy link
Copy Markdown
Contributor Author

hpeebles commented Apr 1, 2026

The benchmarks are failing because there are 2 new benchmarks.

I'm holding off on regenerating the files though until the PR has been approved, so that reviewers have a chance to see the results of the benchmarks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant